from . import api
from flask import request,jsonify,current_app,session
from ihome.utils.response_code import RET
import re
from ihome import redis_store,db,constance
from ihome.models import User
from sqlalchemy.exc import IntegrityError #当数据库报错：Duplicate entry时前端对应的异常对象
# POST http://127.0.0.1:5000/api/v1.0/users
@api.route("/users",methods=["POST"])
def register():
    """
    注册功能
    要求前端传递过来的数据时json格式的数据
    前端需要传递的参数：
    {
        "mobile":"15033889442",
        "sms_code":"3456",
        "password":"12345",
        "password2":"12345"
    }
    :return:
    """
    # 1. 接收前端传递过来的参数
    # request.get_json()
    # 如果想获取前端传递过来的json字符串，则必须设置请求头：Content-Type:"application/json"
    # req_dict = request.json # 将前端传递过来的json字符串接收并转化成python字典
    req_dict = request.get_json() # 将前端传递过来的json字符串接收并转化成python字典
    print("req_dict=",req_dict)
    # print("req_dict=",req_dict,",type=",type(req_dict)) #type= <class 'dict'>
    mobile = req_dict.get("mobile")
    sms_code = req_dict.get("sms_code")
    password = req_dict.get("password")
    password2 = req_dict.get("password2")
    print("mobile=",mobile,",sms_code=",sms_code,",password=",password,",password2=",password2)
    # 2. 校验参数
    if not all([mobile, sms_code, password, password2]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不完整")

    # re.match(r"^1[34578]\d{9}$",mobile)：匹配成功返回匹配器对象，匹配失败返回None
    if not re.match(r"^1[3-9]\d{9}$", mobile): # 如果匹配成功则返回匹配器对象Matcher，否则返回None
        # 表示手机号格式错误
        return jsonify(errno=RET.PARAMERR, errmsg="手机号格式错误")

    if password != password2:
        return jsonify(errno=RET.PARAMERR, errmsg="两次输入密码不一致")

    # 3.执行业务逻辑
    try:
        # 从redis中取出短信验证码
        real_sms_code=redis_store.get("sms_code_%s"%mobile)
        # 判断短信验证码是否过期
        if real_sms_code is None:
            return jsonify(errno=RET.NODATA, errmsg="短信验证码已经失效")
        # 解码数据为字符串
        real_sms_code = real_sms_code.decode("utf-8")
        # 删除短信验证码，防止重复使用
        redis_store.delete("sms_code_%s" % mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="从redis中获取真实手机验证码出现异常")
    # 判断用户填写的短信验证码是否正确
    if real_sms_code != sms_code:
        return jsonify(errno=RET.DATAERR, errmsg="短信验证码错误")

    # # 判断用户的手机号是否注册过
    # try:
    #     user = User.query.filter_by(mobile=mobile).first()
    # except Exception as e:
    #     current_app.logger.error(e)
    #     return jsonify(errno=RET.DBERR, errmsg="根据手机号查询用户时出现异常")
    # else:
    #     if user is not None:
    #         # 表示手机号已经存在
    #         return jsonify(errno=RET.DATAEXIST, errmsg="手机号已经存在")
    # # 保存用户注册的数据到数据库
    # db.session.add(user)
    # db.session.commit()

    # 保存用户注册的数据到数据库
    user = User(name=mobile, mobile=mobile)
    # 密码没有设置，有问题
    user.password=password

    # xx=user.password

    try:
        db.session.add(user)
        db.session.commit()
    except IntegrityError as e:
        # 数据库操作出现错误后回滚
        db.session.rollback()
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAEXIST, errmsg="手机号已经存在")
    except Exception as e:
        # 数据库操作出现错误后回滚
        db.session.rollback()
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询数据库出现异常")

    # 保存登录状态到session中
    session["name"] = mobile
    session["mobile"] = mobile
    session["user_id"] = user.id
    # 4.返回响应结果,{"errno":"0","errmsg":"注册成功"}
    return jsonify(errno=RET.OK, errmsg="注册成功")

# 所谓登录其实是创建一份资源，所以我们要使用post提交
# post http://127.0.0.1:5000/api/v1.0/sessions
@api.route("/sessions",methods=["POST"])
def login():
    """
    完成登录功能
    前端传递过来的json格式的数据为：
    {"mobile":"15033889442","password":"hsjwcf"}

    :return:
    """
    # 1. 获取前端传递过来的参数
    python_dict = request.json
    print("python_dict=",python_dict,",type=",type(python_dict))
    mobile = python_dict.get("mobile")
    password = python_dict.get("password")

    # 2.校验参数
    # 参数完整性校验
    if not all([mobile, password]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不完整")
    # 手机号格式校验
    if not re.match(r"^1[34578]\d{9}$", mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机号格式错误")

    # 3. 执行业务逻辑
    # 3.1 判断错误次数是否超过限制，超过则返回
    # redis记录的格式：access_num_请求的IP:"访问次数"
    # 得到当前客户端的IP地址
    user_ip = request.remote_addr
    try:
        # access_num_127.0.0.1
        access_num = redis_store.get("access_num_%s"%user_ip)
        print("access_num=",access_num)
    except Exception as e:
        current_app.logger.error(e)
    else:
        if access_num is not None and int(access_num)>=constance.LOGIN_ERROR_MAX_TIMES:
            return jsonify(errno=RET.REQERR,errmsg="错误次数过多，请稍后重试！")

    # 3.2 从数据库中根据手机号查询用户对象
    # 从数据库中根据手机号查询用户对象
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="获取用户信息失败")
    # 3.3 用数据库中的密码和前端用户传递的密码进行对比校验
    # 如果验证失败记录错误次数，返回提示信息
    if user is None or not user.check_password(password):
        try:
            # set incr access_num_127.0.0.1 1
            redis_store.incr("access_num_%s"%user_ip)
            redis_store.expire("access_num_%s"%user_ip,constance.LOGIN_ERROR_FORBID_TIME)
        except Exception as e:
            current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg="用户名或者密码错误")
    # 3.4 如果验证成功保存登录状态到session中
    # 如果验证成功保存登录状态到session中
    session["name"] = user.name
    session["mobile"] = mobile
    session["user_id"] = user.id
    # 4. 返回响应结果 {"errno":"0","errmsg":"登录成功"}
    return jsonify(errno=RET.OK, errmsg="登录成功")

# GET http://127.0.0.1:5000/api/v1.0/session
@api.route("/session", methods=["GET"])
def check_login():
    """检查登录状态"""
    # 尝试从session中取出用户名
    name = session.get("name")
    if name is not None:  # 如果成功取出name的值说明登录成功，否则说明未登录
        # {"errno":"0","errmsg":true,"data":{"name":"15033889444"}}
        return jsonify(errno=RET.OK, errmsg=True, data={"name": name})
    else:
        return jsonify(errno=RET.SESSIONERR, errmsg=False)

# DELETE http://127.0.0.1:5000/api/v1.0/session
@api.route("/session", methods=["DELETE"])
def logout():
    """
    注销功能的编写
    :return:
    """
    session.clear()
    return jsonify(errno=RET.OK, errmsg="OK")
